/*
 * StartApplication.java
 *
 * Created on Jun 10, 2010 2:49:36 PM;
 */

package org.sunspotworld;

import com.sun.spot.core.resources.transducers.II2C;
import com.sun.spot.core.resources.transducers.ISwitch;
import com.sun.spot.core.resources.transducers.ITriColorLED;
import com.sun.spot.core.resources.transducers.LEDColor;
import com.sun.spot.edemo.EDemoBoard;
import java.io.IOException;
import javax.microedition.midlet.MIDlet;


import javax.microedition.midlet.MIDletStateChangeException;

/**
 * The startApp method of this class is called by the VM to start the
 * application.
 *
 * The manifest specifies this class as MIDlet-1, which means it will
 * be selected for execution.
 */
public class StartApplication extends MIDlet {

    /**
     * These are the slave addresses of the ADJD-S371 that are either used to
     * read or write. The original slave address is 0x74 which is 1110100 base 2.
     * We have to shift that address to the left one because the LSB is being
     * used to tell the device to read or write.
     *
     * Write = 11101000 >> 0xE8
     * Read = 11101001 >> 0xE9
     */
    private final static byte SLA_ADJDS371_WRITE  = (byte) 0xE8;

    private final static byte SLA_ADJDS371_READ = (byte) 0xE9;

    /**
     * The hex values in byte form that will be used to initialize the
     * color light sensor. CAP = capacitance and INT_HIGH is the first 4 bits of
     * the value for integration time. INT_LOW is the last 8 bits of the value
     * for integration time.
     */
    private final static byte CAP = (byte) 0x0A;

    private final static byte INT_LOW = (byte) 0xAC;
    private final static byte INT_HIGH = (byte) 0x0D;

    /**
     * These are the registers for setting the capacitors for RGB + clear
     */
    private final static byte CAP_RED_REG = (byte) 0x06;

    private final static byte CAP_GREEN_REG = (byte) 0x07;

    private final static byte CAP_BLUE_REG = (byte) 0x08;


    /**
     * These are the registers for setting the integration time slot for RGB + clear
     */
    private final static byte INT_RED_LO_REG = (byte) 0x0A;
    private final static byte INT_RED_HI_REG = (byte) 0x0B;

    private final static byte INT_GREEN_LO_REG = (byte) 0x0C;
    private final static byte INT_GREEN_HI_REG = (byte) 0x0D;

    private final static byte INT_BLUE_LO_REG = (byte) 0x0E;
    private final static byte INT_BLUE_HI_REG = (byte) 0x0F;

    /**
     * These are the registers that hold the data values for RGB + clear
     */
    private final static byte DATA_RED_LO_REG = (byte) 0x40;
    private final static byte DATA_RED_HI_REG = (byte) 0x41;
    private final static byte DATA_GREEN_LO_REG = (byte) 0x42;
    private final static byte DATA_GREEN_HI_REG = (byte) 0x43;
    private final static byte DATA_BLUE_LO_REG = (byte) 0x44;
    private final static byte DATA_BLUE_HI_REG = (byte) 0x45;

    /**
     * The control register is used to tell the I2C to take the RGB + clear
     * readings. You send 0x01 to the ctrl register to make this happen.
     */
    private final static byte CTRL_REG = (byte) 0x00;

    private final static byte CONFIG_REG = (byte) 0x01;

    private final static byte GET_SENSOR_DATA = (byte) 0x01;

    /**
     * The registers that store the values are two bytes long, which is translated
     * into a short value.
     */
    private short red;
    private short green;
    private short blue;

    private int counter = 1;

    /**
     * Boolean to control main process loop
     */
    private boolean TAKE_READING = true;

    //Port # for radio communication
    private String port = "51";

    /**
     * Switches to help control what option to do
     */
    private ISwitch [] sw = EDemoBoard.getInstance().getSwitches();

    /**
     * Used as indicators of the spots status.
     */
    private ITriColorLED[] led = EDemoBoard.getInstance().getLEDs();

    /**
     * The I2C data type
     */
    private II2C i2c = EDemoBoard.getInstance().getI2C();

    private int LEDRed;
    private int LEDBlue;
    private int LEDGreen;

    /**
     * This method will write the variable CAP into the register of the I2C
     * that stores the cap of red.
     */
    private void setCapRed() {
        byte [] data = new byte[1];
        data[0] = CAP;
        try {
            i2c.write(SLA_ADJDS371_WRITE, CAP_RED_REG, 1, data, 0, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }
    }

    /**
     * This method will write the variable CAP into the register of the I2C
     * that stores the cap of green.
     */
    private void setCapGreen() {
        byte [] data = new byte[1];
        data[0] = CAP;
        try {
            i2c.write(SLA_ADJDS371_WRITE, CAP_GREEN_REG, 1, data, 0, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }
    }

    /**
     * This method will write the variable CAP into the register of the I2C
     * that stores the cap of blue.
     */
    private void setCapBlue() {
        byte [] data = new byte[1];
        data[0] = CAP;
        try {
            i2c.write(SLA_ADJDS371_WRITE, CAP_BLUE_REG, 1, data, 0, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }
    }

    /**
     * This method will write the variable INT_HIGH and INT_LOW into the
     * register of the I2C that stores the integration time of red.
     */
    private void setIntRed() {
        byte [] data = new byte[2];
        data[0] = INT_HIGH;
        data[1] = INT_LOW;
        try {
            i2c.write(SLA_ADJDS371_WRITE, INT_RED_HI_REG, 1, data, 0, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }
        try {
            i2c.write(SLA_ADJDS371_WRITE, INT_RED_LO_REG, 1, data, 1, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }
    }

    /**
     * This method will write the variable INT_HIGH and INT_LOW into the
     * register of the I2C that stores the integration time of blue.
     */
    private void setIntBlue() {
        byte [] data = new byte[2];
        data[0] = INT_HIGH;
        data[1] = INT_LOW;
        try {
            i2c.write(SLA_ADJDS371_WRITE, INT_BLUE_HI_REG, 1, data, 0, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }
        try {
            i2c.write(SLA_ADJDS371_WRITE, INT_BLUE_LO_REG, 1, data, 1, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }
    }

    /**
     * This method will write the variable INT_HIGH and INT_LOW into the
     * register of the I2C that stores the integration time of green.
     */
    private void setIntGreen() {
        byte [] data = new byte[2];
        data[0] = INT_HIGH;
        data[1] = INT_LOW;
        try {
            i2c.write(SLA_ADJDS371_WRITE, INT_GREEN_HI_REG, 1, data, 0, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }
        try {
            i2c.write(SLA_ADJDS371_WRITE, INT_GREEN_LO_REG, 1, data, 1, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }
    }

    /**
     * This method will read the register that is holding the value for red.
     * This value is 2 bytes long so we will have to do two read statements
     * to receive the short value.
     * @return red
     */
    private short getRed() {
        byte[] rcv = new byte[2];
        try {
            i2c.read(SLA_ADJDS371_READ, DATA_RED_HI_REG, 1, rcv, 0, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }

        try {
            i2c.read(SLA_ADJDS371_READ, DATA_RED_LO_REG, 1, rcv, 1, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }

        short value = convertByteToShort(rcv);

        return value;
    }

    /**
     * This method will read the register that is holding the value for green.
     * This value is 2 bytes long so we will have to do two read statements
     * to receive the short value.
     * @return green
     */
    private short getGreen() {
        byte[] rcv = new byte[2];
        try {
            i2c.read(SLA_ADJDS371_READ, DATA_GREEN_HI_REG, 1, rcv, 0, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }

        try {
            i2c.read(SLA_ADJDS371_READ, DATA_GREEN_LO_REG, 1, rcv, 1, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }

        short value = convertByteToShort(rcv);

        return value;
    }

    /**
     * This method will read the register that is holding the value for blue.
     * This value is 2 bytes long so we will have to do two read statements
     * to receive the short value.
     * @return blue
     */
    private short getBlue() {
        byte[] rcv = new byte[2];
        try {
            i2c.read(SLA_ADJDS371_READ, DATA_BLUE_HI_REG, 1, rcv, 0, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }

        try {
            i2c.read(SLA_ADJDS371_READ, DATA_BLUE_LO_REG, 1, rcv, 1, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }

        short value = convertByteToShort(rcv);

        return value;
    }

/**
     * This method will set the offsets for the readings according to the light
     * that is already present around the sensor. These offsets are taken by
     * reading in the amount of light that is getting through the pin hole.
     */
    private void getAndTrimOffsets() {
        byte[] data = new byte[2];
        data[0] = (byte) 0x02;
        data[1] = (byte) 0x01;
        try {
            i2c.write(SLA_ADJDS371_WRITE, CTRL_REG, 1, data, 0, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }
        try {
            i2c.write(SLA_ADJDS371_WRITE, CONFIG_REG, 1, data, 1, 1);
        } catch(IOException e) {

        }

        while(i2c.isBusy()) {
            //Do Nothing
        }
    }

    /**
     * This method will send the command to take a sensor reading to the control
     * register. The register will hold the command value given until it is done
     * with taking the reading. The control register will then clear the
     * register when it is complete.
     */
    public void takeSensorReadings() {
        byte[] data = new byte[2];
        data[0] = GET_SENSOR_DATA;
        try {
            i2c.write(SLA_ADJDS371_WRITE, CTRL_REG, 1, data, 0, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }
        try {
            i2c.read(SLA_ADJDS371_READ, CTRL_REG, 1, data, 1, 1);
        } catch(IOException e) {

        }
        while(i2c.isBusy()) {
            //Do Nothing
        }
        //keep checking the control register for 0x00
        while(data[1] != 0x00) {
            try {
                i2c.read(SLA_ADJDS371_READ, CTRL_REG, 1, data, 1, 1);
            } catch(IOException e) {

            }
            while(i2c.isBusy()) {
                //Do Nothing
            }
        }
    }
    /**
     * This method will transform a byte array into a long. The numbers we are
     * working with for this light sensor will not get larger than 2 bytes. In
     * java, that number is to large for an integer so it must be made into a
     * long.
     * @param b the byte array being converted
     * @return the long value of the byte array
     */
    public short convertByteToShort(byte[] b) {
        short l = 0;
        for(int i = 0; i < 2; i++) {
            l <<= 8;
            l ^= (long)b[i] & 0xFF;
        }
        return l;
    }

    protected void startApp() throws MIDletStateChangeException {
        //Open a connection to the I2C
        try {
            i2c.open();
        } catch(IOException e) {
            System.out.println("Could not find the I2C.");
        }
         System.out.println("Running SpotLight.");
        for(int i = 0; i < led.length; i++) {
            led[i].setColor(LEDColor.RED);
            led[i].setOn();
        }
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~");
        System.out.print("Initializing Registers ... ");
        //Set the caps and integration times for the sensor
        setCapRed();
        led[0].setColor(LEDColor.GREEN);

        setCapGreen();
        led[1].setColor(LEDColor.GREEN);

        setCapBlue();
        led[2].setColor(LEDColor.GREEN);

        setIntRed();
        led[3].setColor(LEDColor.GREEN);

        setIntGreen();
        led[4].setColor(LEDColor.GREEN);

        setIntBlue();
        led[5].setColor(LEDColor.GREEN);
        
        led[6].setColor(LEDColor.GREEN);
        led[7].setColor(LEDColor.GREEN);

        System.out.println("Done");
        for(int i = 0; i < led.length; i++) {
            led[i].setColor(LEDColor.YELLOW);
        }
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~");
        System.out.println("Please clear the pin hole of any obstructions.");
        System.out.println("When ready to take offset readings, please press one of the switches.");

        while(sw[0].isOpen() && sw[1].isOpen()) {

        }

        getAndTrimOffsets();

        System.out.println("Offset initialization complete.");

        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~");
        System.out.println("Ready To Take Reading.");

        //Main loop to keep taking values
        //Press both switches to end the application
        while(sw[0].isOpen() || sw[1].isOpen()) {
            //if one of the switches is closed, then take a reading
            if((sw[0].isClosed() || sw[1].isClosed()) && TAKE_READING) {

                for(int i = 0; i < led.length; i++) {
                    led[i].setOff();
                }

                //Tell the I2C to take readings.
                takeSensorReadings();

                //Pull the red value out of the register
                red = getRed();

                //Pull the green value out of the register
                green = getGreen();

                //Pull the blue value out of the register
                blue = getBlue();

                led[7].setColor(LEDColor.GREEN);
                led[7].setOn();

                /**Convert the readings from the I2C to integers the spot can use
                 *
                 * Take out this comment and add in the code that will set LEDRed,
                 * LEDGreen, and LEDBlue into integers that can be used by the
                 * setRGB(int red, int green, int blue) method later on. The
                 * conversion is (255*reading)/1023
                LEDRed
                LEDGreen
                LEDBlue
                */

                //Turn off the leds being used to reproduce the light
                led[0].setOff();
                led[1].setOff();
                led[2].setOff();
                led[3].setOff();

                //Set the color of the leds using the converted values
                led[0].setRGB(LEDRed, LEDGreen, LEDBlue);
                led[1].setRGB(LEDRed, LEDGreen, LEDBlue);
                led[2].setRGB(LEDRed, LEDGreen, LEDBlue);
                led[3].setRGB(LEDRed, LEDGreen, LEDBlue);

                //Turn the leds on
                led[0].setOn();
                led[1].setOn();
                led[2].setOn();
                led[3].setOn();


                System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~");
                System.out.println("Reading # " + counter);
                System.out.println("Red: " + red);
                System.out.println("Green: " + green);
                System.out.println("Blue: " + blue);
                System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~");

                TAKE_READING = false;

                counter++;

                led[7].setOff();

            }

            //This will make sure that only one reading will be taken
            //when the switch is pressed.
            if(sw[0].isOpen() && sw[1].isOpen()) {
                TAKE_READING = true;
            }
        }

        //Once main loop is done, close out the I2C connection
        try {
            i2c.close();
        } catch(IOException e) {
            System.out.println("Could not find the I2C.");
        }

        notifyDestroyed();                      // cause the MIDlet to exit
    }

    protected void pauseApp() {
        // This is not currently called by the Squawk VM
    }

    /**
     * Called if the MIDlet is terminated by the system.
     * I.e. if startApp throws any exception other than MIDletStateChangeException,
     * if the isolate running the MIDlet is killed with Isolate.exit(), or
     * if VM.stopVM() is called.
     *
     * It is not called if MIDlet.notifyDestroyed() was called.
     *
     * @param unconditional If true when this method is called, the MIDlet must
     *    cleanup and release all resources. If false the MIDlet may throw
     *    MIDletStateChangeException  to indicate it does not want to be destroyed
     *    at this time.
     */
    protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

    }
}
